using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.EntityFrameworkCore;
using Volo.Abp.Domain.Repositories.EntityFrameworkCore;
using Volo.Abp.EntityFrameworkCore;
using AuthService.Application.Security;
using AuthService.Domain.Security;

namespace AuthService.EntityFrameworkCore.Security
{
    /// <summary>
    /// 安全事件仓储实现
    /// </summary>
    public class SecurityEventRepository : EfCoreRepository<AuthServiceDbContext, SecurityEvent, Guid>, ISecurityEventRepository
    {
        public SecurityEventRepository(IDbContextProvider<AuthServiceDbContext> dbContextProvider)
            : base(dbContextProvider)
        {
        }

        public async Task<List<SecurityEvent>> GetByUserIdAsync(Guid userId, CancellationToken cancellationToken = default)
        {
            return await DbSet
                .Where(x => x.UserId == userId)
                .OrderByDescending(x => x.EventTime)
                .ToListAsync(GetCancellationToken(cancellationToken));
        }

        public async Task<List<SecurityEvent>> GetHighRiskAssessmentsAsync(Guid? tenantId, CancellationToken cancellationToken = default)
        {
            return await DbSet
                .Where(x => x.RiskLevel == "High" || x.RiskLevel == "Critical" && (!tenantId.HasValue || x.TenantId == tenantId))
                .OrderByDescending(x => x.EventTime)
                .ToListAsync(GetCancellationToken(cancellationToken));
        }

        public async Task<List<SecurityEvent>> GetByDateRangeAsync(DateTime startTime, DateTime endTime, CancellationToken cancellationToken = default)
        {
            return await DbSet
                .Where(x => x.EventTime >= startTime && x.EventTime <= endTime)
                .OrderByDescending(x => x.EventTime)
                .ToListAsync(GetCancellationToken(cancellationToken));
        }

        public async Task<List<SecurityEvent>> GetListAsync(string filter = null, string riskLevel = null, int maxResultCount = 10, int skipCount = 0, CancellationToken cancellationToken = default)
        {
            var query = DbSet.AsQueryable();

            if (!string.IsNullOrEmpty(filter))
            {
                query = query.Where(x =>
                    x.EventType.Contains(filter) ||
                    x.Description.Contains(filter) ||
                    x.IpAddress.Contains(filter));
            }

            if (!string.IsNullOrEmpty(riskLevel))
            {
                query = query.Where(x => x.RiskLevel == riskLevel);
            }

            return await query
                .OrderByDescending(x => x.EventTime)
                .Skip(skipCount)
                .Take(maxResultCount)
                .ToListAsync(GetCancellationToken(cancellationToken));
        }

        public async Task<long> GetCountAsync(string filter = null, string riskLevel = null, CancellationToken cancellationToken = default)
        {
            var query = DbSet.AsQueryable();

            if (!string.IsNullOrEmpty(filter))
            {
                query = query.Where(x =>
                    x.EventType.Contains(filter) ||
                    x.Description.Contains(filter) ||
                    x.IpAddress.Contains(filter));
            }

            if (!string.IsNullOrEmpty(riskLevel))
            {
                query = query.Where(x => x.RiskLevel == riskLevel);
            }

            return await query.CountAsync(cancellationToken);
        }

        public async Task<int> DeleteExpiredAssessmentsAsync(DateTime expireDate, CancellationToken cancellationToken = default)
        {
            var expiredEvents = await DbSet
                .Where(x => x.EventTime < expireDate)
                .ToListAsync(GetCancellationToken(cancellationToken));

            if (expiredEvents.Any())
            {
                await DeleteManyAsync(expiredEvents, autoSave: true, cancellationToken: cancellationToken);
            }

            return expiredEvents.Count;
        }

        public async Task<int> CleanupOldEventsAsync(DateTime beforeDate, CancellationToken cancellationToken = default)
        {
            var oldEvents = await DbSet
                .Where(x => x.EventTime < beforeDate)
                .ToListAsync(GetCancellationToken(cancellationToken));

            if (oldEvents.Any())
            {
                await DeleteManyAsync(oldEvents, autoSave: true, cancellationToken: cancellationToken);
            }

            return oldEvents.Count;
        }

        public async Task<SecurityEvent> GetByEventIdAsync(string eventId, CancellationToken cancellationToken = default)
        {
            return await DbSet
                .FirstOrDefaultAsync(x => x.EventId == eventId, GetCancellationToken(cancellationToken));
        }

        public async Task<List<SecurityEvent>> GetByEventTypeAsync(string eventType, CancellationToken cancellationToken = default)
        {
            return await DbSet
                .Where(x => x.EventType == eventType)
                .OrderByDescending(x => x.EventTime)
                .ToListAsync(GetCancellationToken(cancellationToken));
        }

        public async Task<List<SecurityEvent>> GetByTimeRangeAsync(DateTime startTime, DateTime endTime, string eventType = null, Guid? userId = null, CancellationToken cancellationToken = default)
        {
            var query = DbSet.Where(x => x.EventTime >= startTime && x.EventTime <= endTime);

            if (!string.IsNullOrEmpty(eventType))
            {
                query = query.Where(x => x.EventType == eventType);
            }

            if (userId.HasValue)
            {
                query = query.Where(x => x.UserId == userId.Value);
            }

            return await query
                .OrderByDescending(x => x.EventTime)
                .ToListAsync(GetCancellationToken(cancellationToken));
        }

        public async Task<List<SecurityEvent>> GetByIpAddressAsync(string ipAddress, CancellationToken cancellationToken = default)
        {
            return await DbSet
                .Where(x => x.IpAddress == ipAddress)
                .OrderByDescending(x => x.EventTime)
                .ToListAsync(GetCancellationToken(cancellationToken));
        }

        public async Task<List<SecurityEvent>> GetUnprocessedEventsAsync(CancellationToken cancellationToken = default)
        {
            return await DbSet
                .Where(x => !x.IsProcessed)
                .OrderByDescending(x => x.EventTime)
                .ToListAsync(GetCancellationToken(cancellationToken));
        }

        public async Task<List<SecurityEvent>> GetHighRiskEventsAsync(CancellationToken cancellationToken = default)
        {
            return await DbSet
                .Where(x => x.RiskLevel == "High" || x.RiskLevel == "Critical")
                .OrderByDescending(x => x.EventTime)
                .ToListAsync(GetCancellationToken(cancellationToken));
        }

        public async Task<List<SecurityEvent>> GetFailedEventsAsync(CancellationToken cancellationToken = default)
        {
            return await DbSet
                .Where(x => !x.IsSuccess)
                .OrderByDescending(x => x.EventTime)
                .ToListAsync(GetCancellationToken(cancellationToken));
        }

        public async Task MarkEventsAsProcessedAsync(List<Guid> eventIds, string processedBy = null, CancellationToken cancellationToken = default)
        {
            var events = await DbSet
                .Where(x => eventIds.Contains(x.Id))
                .ToListAsync(GetCancellationToken(cancellationToken));

            foreach (var @event in events)
            {
                @event.IsProcessed = true;
                @event.ProcessedTime = DateTime.Now;
                @event.ProcessedBy = processedBy;
            }

            await SaveChangesAsync(cancellationToken);
        }

        public async Task<SecurityEventStatisticsDto> GetEventStatisticsAsync(DateTime? startTime = null, DateTime? endTime = null, Guid? userId = null, string eventType = null, CancellationToken cancellationToken = default)
        {
            var query = DbSet.AsQueryable();

            if (startTime.HasValue)
            {
                query = query.Where(x => x.EventTime >= startTime.Value);
            }

            if (endTime.HasValue)
            {
                query = query.Where(x => x.EventTime <= endTime.Value);
            }

            if (userId.HasValue)
            {
                query = query.Where(x => x.UserId == userId.Value);
            }

            if (!string.IsNullOrEmpty(eventType))
            {
                query = query.Where(x => x.EventType == eventType);
            }

            var events = await query.ToListAsync(cancellationToken);

            return new SecurityEventStatisticsDto
            {
                TotalEvents = events.Count,
                SuccessEvents = events.Count(x => x.IsSuccess),
                FailedEvents = events.Count(x => !x.IsSuccess),
                HighRiskEvents = events.Count(x => x.RiskLevel == "High" || x.RiskLevel == "Critical"),
                UnprocessedEvents = events.Count(x => !x.IsProcessed),
                EventsByType = events.GroupBy(x => x.EventType).ToDictionary(g => g.Key, g => g.Count()),
                EventsByRiskLevel = events.GroupBy(x => x.RiskLevel).ToDictionary(g => g.Key, g => g.Count()),
                EventsByDate = events.GroupBy(x => x.EventTime.Date).ToDictionary(g => g.Key, g => g.Count())
            };
        }
    }
}